#include <mathematics/Vector4.h>

namespace ne
{
    const Vector4 Vector4::ZERO(0.0f);
    const Vector4 Vector4::ONE(1.0f);
    const Vector4 Vector4::UNIT_X(1.0f, 0.0f, 0.0f, 0.0f);
    const Vector4 Vector4::UNIT_Y(0.0f, 1.0f, 0.0f, 0.0f);
    const Vector4 Vector4::UNIT_Z(0.0f, 0.0f, 1.0f, 0.0f);
    const Vector4 Vector4::UNIT_W(0.0f, 0.0f, 0.0f, 1.0f);
    const Vector4 Vector4::NEGATIVE_ONE(-1.0f);
    const Vector4 Vector4::NEGATIVE_UNIT_X(-1.0f, 0.0f, 0.0f, 0.0f);
    const Vector4 Vector4::NEGATIVE_UNIT_Y(0.0f, -1.0f, 0.0f, 0.0f);
    const Vector4 Vector4::NEGATIVE_UNIT_Z(0.0f, 0.0f, -1.0f, 0.0f);
    const Vector4 Vector4::NEGATIVE_UNIT_W(0.0f, 0.0f, 0.0f, -1.0f);

    Vector4 operator + (const real vl, const Vector4 &vr)
    {
        return Vector4(vl + vr.x, vl + vr.y, vl + vr.z, vl + vr.w);
    }

    Vector4 operator - (const real vl, const Vector4 & vr)
    {
        return Vector4(vl - vr.x, vl - vr.y, vl - vr.z, vl - vr.w);
    }

    Vector4 operator * (const real vl, const Vector4 &vr)
    {
        return Vector4(vl * vr.x, vl * vr.y, vl * vr.z, vl * vr.w);
    }

    Vector4 operator / (const real vl, const Vector4 &vr)
    {
        assert((vr.x != 0.0f) && (vr.y != 0.0f) && (vr.z != 0.0f) && (vr.w != 0.0f));
        return Vector4(vl / vr.x, vl / vr.y, vl / vr.z, vl / vr.w);
    }

    std::ostream& operator << (std::ostream &o, const Vector4 &v)
    {
        o << "Vector4(x=" << v.x <<
                   ", y=" << v.y <<
                   ", z=" << v.z <<
                   ", w=" << v.w << ")";
        return o;
    }

    bool Vector4::Equal(const Vector4 &vS, const Vector4 &vD, const real tolerance)
    {
        return (Math::Equal(vS.x, vD.x, tolerance) &&
                Math::Equal(vS.y, vD.y, tolerance) &&
                Math::Equal(vS.z, vD.z, tolerance) &&
                Math::Equal(vS.w, vD.w, tolerance));
    }

    Vector4 Vector4::Mid(const Vector4 &vS, const Vector4 &vD)
    {
        return (vS + vD) * 0.5f;
    }

    Vector4 Vector4::Lerp(const Vector4 &vS, const Vector4 &vD, const real u)
    {
        return vS + (vD - vS) * u;
    }

    Vector4 Vector4::Bezier(const Vector4 &vS, const Vector4 &vA, const Vector4 &vB, const Vector4 &vD, const real u)
    {
        Vector4 a = vD - vS + vA * 3.0f - vB * 3.0f;
        Vector4 b = vS * 3.0f - vA * 6.0f + vB * 3.0f;
        Vector4 c = vA * 3.0f - vS * 3.0f;
        real uu = u * u;
        real uuu = uu * u;
        return (a * uuu) + (b * uu) + (c * u) + vS;
    }
}
